﻿using System;
using System.Net;
using System.ServiceModel.Syndication;
using System.Xml;

namespace Jacobi.Seesmic.FeedReader.Model
{
    /// <summary>
    /// A base class for a single feed.
    /// </summary>
    /// <remarks>
    /// The class manages loading the syndication feed and formatting it to a <see cref="TimelineFeed"/>.
    /// </remarks>
    internal abstract class FeedSource
    {
        /// <summary>
        /// Only allow construction within this assembly.
        /// </summary>
        /// <param name="feedUrl">The url for the feed. Must not be null or empty.</param>
        internal FeedSource(string feedUrl)
        {
            FeedUrl = new Uri(feedUrl);
        }

        /// <summary>
        /// Gets the <see cref="Uri"/> this instance was constructed with.
        /// </summary>
        public Uri FeedUrl { get; set; }

        private bool _loading;

        /// <summary>
        /// Starts an async load operation to bring in the feed data.
        /// </summary>
        /// <returns>Returns true when a load operation in initiated.</returns>
        /// <remarks>
        /// Will not start a load operation when one is already in progress.
        /// </remarks>
        public bool BeginLoad()
        {
            if (_loading) return false;

            _loading = true;
            var client = new WebClient();

            client.OpenReadCompleted += (sender, e) =>
                {
                    _loading = false;

                    TimelineFeed feed = null;

                    if (!e.Cancelled &&
                        e.Error == null &&
                        e.Result != null)
                    {
                        feed = ReadFeed(XmlReader.Create(e.Result));
                    }

                    var eventArgs = new LoadCompletedEventArgs(feed, e.Error, e.Cancelled, e.UserState);

                    OnLoadCompleted(eventArgs);
                };

            client.OpenReadAsync(FeedUrl);

            return true;
        }

        /// <summary>
        /// Raises the <see cref="LoadCompleted"/> event.
        /// </summary>
        /// <param name="eventArgs">The feed that has been loaded.</param>
        protected virtual void OnLoadCompleted(LoadCompletedEventArgs eventArgs)
        {
            var handler = LoadCompleted;

            if (handler != null)
            {
                handler(this, eventArgs);
            }
        }

        /// <summary>
        /// Raised when the <see cref="FeedSource"/> has completed loading the feed data.
        /// </summary>
        public event EventHandler<LoadCompletedEventArgs> LoadCompleted;

        /// <summary>
        /// Deserializes the feed from xml to a <see cref="TimelineFeed"/> object.
        /// </summary>
        /// <param name="xmlReader">Must not be null.</param>
        /// <returns>Returns null when the xml could not be interpreted.</returns>
        private TimelineFeed ReadFeed(XmlReader xmlReader)
        {
            var formatter = CreateFeedFormatter();

            if (formatter.CanRead(xmlReader))
            {
                formatter.ReadFrom(xmlReader);

                return (TimelineFeed)formatter.Feed;
            }

            return null;
        }

        /// <summary>
        /// Derived classes implement to create the specialized feed formatter object.
        /// </summary>
        /// <returns>Never returns null.</returns>
        protected abstract SyndicationFeedFormatter CreateFeedFormatter();
    }
}